home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 5
/
MacMania 5.toast
/
/
Internet software
/
NewsWatcher
/
NW Source
/
Source
/
send.c
< prev
next >
Wrap
Text File
|
1997-01-09
|
20KB
|
798 lines
/*----------------------------------------------------------------------------
send.c
This module handles sending postings and mail messages.
Copyright © 1994-1997, Northwestern University.
----------------------------------------------------------------------------*/
#include <string.h>
#include "glob.h"
#include "dialog.h"
#include "news.h"
#include "send.h"
#include "smtp.h"
#include "header.h"
#include "newswatcher.h"
#include "status.h"
#include "message.h"
#include "charset.h"
#include "cancel.h"
#include "memutil.h"
#include "strutil.h"
#include "ic.h"
#define kPostAlert 135 /* Post confirm dialog */
#define kPostCancelAlert 147 /* Post cancel dialog */
/*----------------------------------------------------------------------------
Wrap
Wraps paragraphs to lines of at most 74 characters long.
Entry: text = handle to text to be wrapped.
start = offset in text of beginning of text to be wrapped.
end = offset in text of end of text to be wrapped.
Only lines which which exceed 80 characters in length are wrapped.
----------------------------------------------------------------------------*/
void Wrap (Handle text, long start, long end)
{
char *p, *pEnd, *q, *lastSpace;
p = *text + start;
pEnd = *text + end;
while (p < pEnd) {
q = p;
while (q < pEnd && *q != CR) q++;
if (q - p > 80) {
q = p;
lastSpace = 0;
while (true) {
while (q < pEnd && *q != ' ' && *q != CR) q++;
if (q < pEnd && *q == ' ') {
while (q < pEnd && *q == ' ') q++;
q--;
}
if (q - p >= 75 && lastSpace != 0) {
*lastSpace = CR;
p = lastSpace + 1;
lastSpace = 0;
}
if (q >= pEnd || *q == CR) break;
lastSpace = q;
q++;
}
}
p = q+1;
}
}
/*----------------------------------------------------------------------------
UnWrap
Unwraps paragraphs.
Entry: text = handle to text to be unwrapped.
start = offset in text of beginning of text to be unwrapped.
end = offset in text of end of text to be unwrapped.
----------------------------------------------------------------------------*/
void UnWrap (Handle text, long start, long end)
{
char *p, *pEnd;
p = *text + start;
pEnd = *text + end;
while (p < pEnd) {
while (p < pEnd && *p != CR) p++;
p++;
if (p < pEnd) {
if (*p == ' ' || *p == '\t') continue;
if (*p == CR) {
while (p < pEnd && *p == CR) p++;
continue;
}
*(p-1) = ' ';
}
}
}
/*----------------------------------------------------------------------------
GetRecipients
Gets recipient addresses for an email message.
Entry: text = message to be mailed, including headers.
hdr = C-format message header to process (To, Cc, or Bcc).
Exit: function result = error code.
*recipients = handle to comma-separated list of
recipient email addresses, or nil if header not found.
----------------------------------------------------------------------------*/
static OSErr GetRecipients (Handle text, char *hdr, Handle *recipients)
{
Handle recip;
OSErr err = noErr;
char *p, *pEnd, *q, *r, *begin, *end;
long len;
short parenLevel;
Boolean insideQuotedString = false;
err = FindHeaderHandle(text, hdr, &recip);
if (err != noErr) return err;
if (recip == nil) {
*recipients = nil;
return noErr;
}
/* Strip comments in parentheses and blanks. */
p = q = *recip;
len = MyGetHandleSize(recip);
pEnd = p + len;
while (p < pEnd) {
if (*p == '(') {
parenLevel = 1;
p++;
while (p < pEnd && parenLevel > 0) {
if (*p == '(') {
parenLevel++;
} else if (*p == ')') {
parenLevel--;
}
p++;
}
} else if (*p == ' ') {
p++;
} else {
*q++ = *p++;
}
}
len = q - *recip;
/* Extract recipients */
p = q = *recip;
pEnd = p + len;
while (p < pEnd) {
r = p;
while (r < pEnd) {
if (*r == '"') {
insideQuotedString = true;
r++;
while (r < pEnd && *r != '"') r++;
if (r >= pEnd) break;
insideQuotedString = false;
r++;
} else if (*r == '<' || *r == ',') {
break;
} else {
r++;
}
}
if (insideQuotedString) break;
if (r >= pEnd || *r == ',') {
begin = p;
end = r-1;
} else {
r++;
begin = r;
while (r < pEnd && *r != '>') r++;
end = r-1;
}
p = r;
while (p < pEnd && *p != ',') p++;
if (*p == ',') p++;
len = end - begin + 1;
if (q != *recip) *q++ = ',';
BlockMoveData(begin, q, len);
q += len;
}
len = q - *recip;
MySetHandleSize(recip, len);
*recipients = recip;
return noErr;
}
/*----------------------------------------------------------------------------
SendMailMessage
Send a mail message.
Entry: text = message to be mailed, including headers.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr SendMailMessage (Handle text)
{
SmtpStreamRef s;
OSErr err = noErr;
Handle to = nil;
Handle cc = nil;
Handle bcc = nil;
long toLen, ccLen, bccLen, len;
char *q;
NetServerErrInfo serverErrInfo;
MyICReadSharedPrefs(kICEmail);
MyICReadSharedPrefs(kICSMTPHost);
err = GetRecipients(text, "To", &to);
if (err != noErr) goto exit1;
err = GetRecipients(text, "Cc", &cc);
if (err != noErr) goto exit1;
err = GetRecipients(text, "Bcc", &bcc);
if (err != noErr) goto exit1;
DeleteHeaderLine(text, "Bcc"); /* Don't actually send "Bcc" header line! */
toLen = to == nil ? 0 : MyGetHandleSize(to);
ccLen = cc == nil ? 0 : MyGetHandleSize(cc);
bccLen = bcc == nil ? 0 : MyGetHandleSize(bcc);
len = toLen;
if (cc != nil) {
if (len > 0) len++;
len += ccLen;
}
if (bcc != nil) {
if (len > 0) len++;
len += bccLen;
}
len++;
if (to == nil) {
err = MyNewHandle(len, &to);
} else {
err = MySetHandleSize(to, len);
}
if (err != noErr) goto exit1;
q = *to + toLen;
if (cc != nil) {
if (q > *to) *q++ = ',';
BlockMoveData(*cc, q, ccLen);
q += ccLen;
}
if (bcc != nil) {
if (q > *to) *q++ = ',';
BlockMoveData(*bcc, q, bccLen);
q += bccLen;
}
*q = 0;
p2cstr(gPrefs.mailServerName);
err = SmtpOpen((char*)gPrefs.mailServerName, &s);
c2pstr((char*)gPrefs.mailServerName);
if (err != noErr) goto exit2;
MyHLock(to);
err = SmtpSendMessage(s, gPrefs.emailAddress, *to, text);
MyHUnlock(to);
if (err != noErr) goto exit2;
err = SmtpClose(s);
if (err != noErr) goto exit2;
MyDisposeHandle(to);
MyDisposeHandle(cc);
MyDisposeHandle(bcc);
return noErr;
exit1:
MyDisposeHandle(to);
MyDisposeHandle(cc);
MyDisposeHandle(bcc);
return err;
exit2:
MyDisposeHandle(to);
MyDisposeHandle(cc);
MyDisposeHandle(bcc);
if (err == smtpServerErr) {
SmtpGetServerErrInfo(s, &serverErrInfo);
SmtpClose(s);
ServerErrorMessage(kStrMail, serverErrInfo.command, serverErrInfo.response);
return userCanceledErr;
} else {
p2cstr(gPrefs.mailServerName);
SaveNetErrorInfo(kStrMail, (char*)gPrefs.mailServerName);
c2pstr((char*)gPrefs.mailServerName);
return err;
}
}
/*----------------------------------------------------------------------------
AreYouSure
Present "Are you sure you want to post" alert.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr AreYouSure (void)
{
DialogPtr dlg = nil;
short item;
OSErr err = noErr;
if (!gPrefs.areYouSureAlert) return noErr;
err = MyGetNewDialog(kPostAlert, ok, cancel, &dlg);
if (err != noErr) return err;
SysBeep(0);
MyModalDialog(dlg, gDialogFilterUPP, &item);
err = DoClose(dlg);
if (err != noErr) return err;
return item == ok ? noErr : userCanceledErr;
}
/*----------------------------------------------------------------------------
BuildMessageForPrinting
Build a message for printing.
Entry: wind = pointer to message window.
Exit: function result = error code.
*messageText = handle to message text to be printed.
----------------------------------------------------------------------------*/
OSErr BuildMessageForPrinting (WindowPtr wind, Handle *messageText)
{
TWindow **info;
TEHandle theTE;
Handle text = nil;
Handle msg = nil;
Handle newsgroups, to, subject, cc, bcc, replyto;
Handle followupto, keywords, distribution;
Handle extraMail, signature, references;
OSErr err = noErr, err1 = noErr;
long sigLen, textLen, newTextLen;
char *q;
Boolean newsIcon, mailIcon, selfIcon;
info = (TWindow**)GetWRefCon(wind);
theTE = (**info).theTE;
text = (**theTE).hText;
newsgroups = (**(**info).newsgroupsField).hText;
to = (**(**info).toField).hText;
subject = (**(**info).subjectField).hText;
cc = (**(**info).ccField).hText;
bcc = (**(**info).bccField).hText;
replyto = (**(**info).replytoField).hText;
followupto = (**(**info).followuptoField).hText;
keywords = (**(**info).keywordsField).hText;
distribution = (**(**info).distributionField).hText;
extraMail = (**(**info).extraMailField).hText;
signature = (**(**info).signatureField).hText;
references = (**info).references;
newsIcon = (**info).newsIcon;
mailIcon = (**info).mailIcon;
selfIcon = (**info).selfIcon;
err = MyHandToHand(&text);
if (err != noErr) return err;
textLen = MyGetHandleSize(text);
q = *text + textLen - 1;
while (q > *text && isLWSPorCR(*q)) q--;
q++;
newTextLen = q - *text;
if (newTextLen != textLen) {
textLen = newTextLen;
MySetHandleSize(text, textLen);
}
sigLen = MyGetHandleSize(signature);
if (sigLen > 0) {
newTextLen = textLen + sigLen + 1;
err = MySetHandleSize(text, newTextLen);
if (err != noErr) goto exit;
q = *text + textLen;
*q++ = CR;
BlockMoveData(*signature, q, sigLen);
textLen = newTextLen;
}
if (textLen == 0) {
textLen = 1;
err = MySetHandleSize(text, textLen);
if (err != noErr) goto exit;
**text = ' ';
}
if (!mailIcon) to = cc = bcc = nil;
if (!newsIcon) newsgroups = followupto = distribution = nil;
err = MakeMailHeader(subject, to, cc, bcc, (**info).from,
(**info).selfIcon, replyto, keywords, extraMail,
newsgroups, followupto, distribution,
references, &msg);
if (err != noErr) goto exit;
err = MyHandAndHand(text, msg);
if (err != noErr) goto exit;
*messageText = msg;
MyDisposeHandle(text);
return noErr;
exit:
MyDisposeHandle(text);
MyDisposeHandle(msg);
return err;
}
/*----------------------------------------------------------------------------
MessageBodyContainsOnlyQuotedText
Check to see if a message body contains only quoted text.
Entry: text = handle to message body text.
quoteString = handle to quote string.
Exit: function result = true if message body contains only quoted text
and blank lines (not counting the beginning attribution line).
----------------------------------------------------------------------------*/
static Boolean MessageBodyContainsOnlyQuotedText (Handle text, Handle quoteString)
{
long textLen, quoteStringLen;
char *p, *pEnd, *r;
CStr255 attributionStr1, attributionStr3;
short attributionStr1Len, attributionStr3Len;
textLen = MyGetHandleSize(text);
quoteStringLen = MyGetHandleSize(quoteString);
if (quoteStringLen == 0) return false;
GetCString(kStrQuoteStr1, attributionStr1);
GetCString(kStrQuoteStr3, attributionStr3);
attributionStr1Len = strlen(attributionStr1);
attributionStr3Len = strlen(attributionStr3);
p = *text;
pEnd = p + textLen;
/* Check the first line of the message body. If it is the attribution line
"In article <...>, so-and-so wrote:", then skip over it. */
r = p;
while (r < pEnd && *r != CR) r++;
if (r - p > attributionStr1Len + attributionStr3Len + 10) {
if (MyStrNEqual(p, attributionStr1, attributionStr1Len) &&
*(p + attributionStr1Len) == '<' &&
MyStrNEqual(r - attributionStr3Len, attributionStr3, attributionStr3Len))
{
p = r+1;
}
}
/* Check the remaining lines of the message body for only quoted and blank
lines. */
while (p < pEnd) {
r = p;
while (r < pEnd && isLWSP(*r)) r++;
if (r >= pEnd) return true;
if (*r == CR) { /* all blank line */
p = r+1;
continue;
}
if (p + quoteStringLen > pEnd) return false;
if (!MyStrNEqual(p, *quoteString, quoteStringLen)) return false;
p = r+1;
while (p < pEnd && *p != CR) p++;
p++;
}
return true;
}
/*----------------------------------------------------------------------------
MakeAlsoPostedToText
Construct the "(A copy of this message was also posted to...)" text for
a mail message which is also being posted.
Entry: headers = handle to header text for mail message.
Exit: function result = error code.
*alsoPostedTo = handle to extra "also posted to" text to
be added to beginning of mail message.
----------------------------------------------------------------------------*/
static OSErr MakeAlsoPostedToText (Handle headers, Handle *alsoPostedTo)
{
CStr255 str;
OSErr err = noErr;
Handle h = nil;
Handle newsgroups = nil;
GetCString(kStrCopyAlsoPostedTo, str);
err = MyPtrToHand(str, &h, strlen(str));
if (err != noErr) goto exit;
err = FindHeaderHandle(headers, "Newsgroups", &newsgroups);
if (err != noErr) goto exit;
if (newsgroups != nil) {
Munger(newsgroups, 0, ",", 1, ", ", 2);
err = MemError();
if (err != noErr) goto exit;
err = MyHandAndHand(newsgroups, h);
if (err != noErr) goto exit;
}
err = MyStrAndHand(")\r\r", h);
if (err != noErr) goto exit;
Wrap(h, 0, MyGetHandleSize(h));
MyDisposeHandle(newsgroups);
*alsoPostedTo = h;
return noErr;
exit:
MyDisposeHandle(h);
MyDisposeHandle(newsgroups);
return err;
}
/*----------------------------------------------------------------------------
SendMessage
Send a message.
Entry: wind = pointer to message window.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr SendMessage (WindowPtr wind)
{
TWindow **info;
TEHandle theTE;
Handle text = nil;
Handle msg = nil;
Handle newsgroups, to, subject, cc, bcc, replyto;
Handle followupto, keywords, distribution;
Handle extraNews, extraMail, signature, references;
Handle alsoPostedTo = nil;
OSErr err = noErr, err1 = noErr;
long sigLen, textLen, newTextLen;
char *q;
Boolean newsIcon, mailIcon, selfIcon;
short item;
CStr255 idStr;
CStr255 statusMsg;
FSSpec fSpec;
Boolean wasChanged;
Boolean posted = false, postIndeterminate = false, mailStarted = false;
DialogPtr dlg = nil;
info = (TWindow**)GetWRefCon(wind);
theTE = (**info).theTE;
text = (**theTE).hText;
newsgroups = (**(**info).newsgroupsField).hText;
to = (**(**info).toField).hText;
subject = (**(**info).subjectField).hText;
cc = (**(**info).ccField).hText;
bcc = (**(**info).bccField).hText;
replyto = (**(**info).replytoField).hText;
followupto = (**(**info).followuptoField).hText;
keywords = (**(**info).keywordsField).hText;
distribution = (**(**info).distributionField).hText;
extraNews = (**(**info).extraNewsField).hText;
extraMail = (**(**info).extraMailField).hText;
signature = (**(**info).signatureField).hText;
references = (**info).references;
newsIcon = (**info).newsIcon;
mailIcon = (**info).mailIcon;
selfIcon = (**info).selfIcon;
if (!newsIcon && !mailIcon && !selfIcon) {
ErrorMessageNumber(kStrMustCheckAnIcon);
return userCanceledErr;
}
if (HeaderLineIsEmpty(subject)) {
ErrorMessageNumber(kStrMustSupplySubject);
return userCanceledErr;
}
if (newsIcon && HeaderLineIsEmpty(newsgroups)) {
ErrorMessageNumber(kStrMustSupplyNewsgroup);
return userCanceledErr;
}
if (mailIcon && !selfIcon &&
HeaderLineIsEmpty(to) && HeaderLineIsEmpty(cc) && HeaderLineIsEmpty(bcc))
{
ErrorMessageNumber(kStrMustSupplyRecipient);
return userCanceledErr;
}
err = MyHandToHand(&text);
if (err != noErr) return err;
textLen = MyGetHandleSize(text);
q = *text + textLen - 1;
while (q > *text && isLWSPorCR(*q)) q--;
q++;
newTextLen = q - *text;
if (newTextLen != textLen) {
textLen = newTextLen;
MySetHandleSize(text, textLen);
}
if (textLen == 0) {
ErrorMessageNumber(kStrMustSupplyBody);
err = userCanceledErr;
goto exit;
}
if (newsIcon && (**info).isFollowup &&
MessageBodyContainsOnlyQuotedText(text, (**(**info).quoteStringField).hText))
{
ErrorMessageNumber(kStrOnlyQuotedText);
err = userCanceledErr;
goto exit;
}
if (newsIcon) {
err = AreYouSure();
if (err != noErr) goto exit;
}
if ((**info).wrapOnSend) Wrap(text, 0, textLen);
sigLen = MyGetHandleSize(signature);
if (sigLen > 0) {
newTextLen = textLen + sigLen + 1;
err = MySetHandleSize(text, newTextLen);
if (err != noErr) goto exit;
q = *text + textLen;
*q++ = CR;
BlockMoveData(*signature, q, sigLen);
textLen = newTextLen;
}
if (newsIcon) {
GetCString(kStrPostingMessageStatusMsg, statusMsg);
err = DisplayStatusMessage(statusMsg);
if (err != noErr) goto exit;
err = MakeNewsHeader(newsgroups, subject, replyto,
followupto, keywords, distribution, extraNews,
nil, references, &msg);
if (err != noErr) goto exit;
FindHeaderCString(msg, "Message-ID", idStr, sizeof(idStr));
err = MyHandAndHand(text, msg);
if (err != noErr) goto exit;
MapMacToLatin1Handle(msg);
err = PostArticle(msg, statusMsg, &postIndeterminate);
posted = err == noErr || postIndeterminate;
if (err != noErr) goto exit;
MyDisposeHandle(msg);
msg = nil;
}
if (mailIcon || selfIcon) {
mailStarted = true;
err = DisplayStatusMessageNumber(kStrMailingMessageStatusMsg);
if (err != noErr) goto exit;
if (!mailIcon) to = cc = bcc = nil;
if (!newsIcon) newsgroups = followupto = distribution = nil;
err = MakeMailHeader(subject, to, cc, bcc, (**info).from,
(**info).selfIcon, replyto, keywords, extraMail,
newsgroups, followupto, distribution,
references, &msg);
if (err != noErr) goto exit;
if (mailIcon && newsIcon) {
err = MakeAlsoPostedToText(msg, &alsoPostedTo);
if (err != noErr) goto exit;
err = MyHandAndHand(alsoPostedTo, msg);
if (err != noErr) goto exit;
MyDisposeHandle(alsoPostedTo);
alsoPostedTo = nil;
}
err = MyHandAndHand(text, msg);
if (err != noErr) goto exit;
MapMacToLatin1Handle(msg);
err = SendMailMessage(msg);
if (err != noErr) goto exit;
}
(**info).changed = false;
MyDisposeHandle(text);
MyDisposeHandle(msg);
if ((**info).alias != nil && gPrefs.savedMsgDelAfterSend) {
err = ResolveAlias(nil, (**info).alias, &fSpec, &wasChanged);
if (err == noErr) {
(**info).alias = nil;
err = FSpDelete(&fSpec);
if (err != noErr) return err;
}
}
return noErr;
exit:
MyDisposeHandle(text);
MyDisposeHandle(msg);
MyDisposeHandle(alsoPostedTo);
if (gCancel && posted) {
gCancel = false;
err1 = MyGetNewDialog(kPostCancelAlert, ok, cancel, &dlg);
if (err1 != noErr) return err1;
SysBeep(0);
MyModalDialog(dlg, gDialogFilterUPP, &item);
err1 = DoClose(dlg);
if (err1 != noErr) return err1;
if (item == cancel) {
GetCString(kStrCancelingStatusMsg, statusMsg);
err1 = DisplayStatusMessage(statusMsg);
if (err1 != noErr) return err1;
err1 = CancelArticle(idStr, newsgroups, statusMsg);
if (err1 != noErr) return err1;
} else {
UncheckNewsIcon(wind);
}
} else if (posted && mailStarted) {
UncheckNewsIcon(wind);
ReportSystemError(err);
NoteMessageNumber(kStrPostOKMailErr);
return userCanceledErr;
}
return err;
}
/*----------------------------------------------------------------------------
SendMessageAndCloseWindow
Send a message and close the message window.
Entry: wind = pointer to message window.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr SendMessageAndCloseWindow (WindowPtr wind)
{
OSErr err = noErr;
err = SendMessage(wind);
if (err != noErr) return err;
return DoClose(wind);
}